package pl.koszolko.documentclustering.cluster;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import pl.koszolko.documentclustering.cluster.document.DocumentForClustering;

public class Dendrogram implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private List<Node> clusters;
	private AtomicInteger countClusters;
	
	public Dendrogram() {
		clusters = new ArrayList<Dendrogram.Node>();
		countClusters = new AtomicInteger();
		countClusters.set(0);
	}	
	
	public List<Node> getClusters() {
		return clusters;
	}
	
	public void setInitialClusters(List<Cluster> clusters) {
		for(Cluster c : clusters) {
			this.clusters.add(new Node(c));
		}
	}
	
	public List<Cluster> getClustersToMarge() {
		List<Cluster> clustersToMargeClusters = new ArrayList<Cluster>();
		Node tmpParent;
		
		for(Node n : clusters) {
			tmpParent = n.getParent();
			if(tmpParent == null)
				if(!clustersToMargeClusters.contains(n.getCluster()))
					clustersToMargeClusters.add(n.getCluster());
			else {
				while(tmpParent.getParent() != null)
					tmpParent = tmpParent.getParent();
				if(!clustersToMargeClusters.contains(tmpParent.getCluster()))
					clustersToMargeClusters.add(tmpParent.getCluster());
			}
		}
		
		//jezeli jest tylko jeden klaster to znaczy ze proces grupowania jest skonczony
		if(clustersToMargeClusters.size() == 1)
			clustersToMargeClusters.clear();
		
		return clustersToMargeClusters;
	}
	
	public void addNewCluster(Cluster newCluster, Cluster child1, Cluster child2, double similarity) {
		
		//utworzenie nowego wezla
		Node newNode = new Node();
		newNode.setCluster(newCluster);
		newNode.setNr(countClusters.getAndIncrement());
		newNode.setSimilarity(similarity);
		
		//dodanie noweg wezla
		clusters.add(newNode);
		
		//ustawienie pol 'parent' dla child1, child2
		Node child1Node = findNode(child1);
		Node child2Node = findNode(child2);
		
		child1Node.setParent(newNode);
		child2Node.setParent(newNode);
	
	}
	
	private Node findNode(Cluster cluster) {
		Node tmp = null;
		for(int i=0 ; i<clusters.size() ; ++i) {
			tmp = clusters.get(i);
			if(tmp.getCluster().equals(cluster))
				return tmp;
		}
		return tmp;
	}
	
	public List<Node> getInitialClusters() {
		List<Node> initialClusters = new ArrayList<Node>();
		
		for(Node n : getClusters()) {
			if(n.getCluster().getDocuments().size() == 1) {
				initialClusters.add(n);
			}
		}
		
		return initialClusters;
	}
	
	@Override
	public String toString() {
		String string = new String();
		string += "Dendrogram:\n";
		
		if(clusters.isEmpty())
			string += "Brak klastrow";
		
		for(Node n : clusters) {
			string += "------------------------------\n";
			string += n.getCluster().toString() + "\n";
			string += "parent:\n";
			if(n.getParent() != null)
				string += n.getParent().getCluster().toString() + "\n";
			string += "------------------------------\n";
		}
		
		return string;
	}
	public class Node implements Serializable, Comparable<Node> {
		private static final long serialVersionUID = 1L;
		
		private Cluster cluster;
		private Node parent;
		private int nr;
		private Double similarity;

		public Node() {
			similarity = 0.0;
		}
		
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + nr;
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Node other = (Node) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (nr != other.nr)
				return false;
			return true;
		}

		public Node(Cluster cluster) {
			this();
			this.cluster = cluster;
		}
		
		public void setCluster(Cluster cluster) {
			this.cluster = cluster;
		}
		
		public Cluster getCluster() {
			return cluster;
		}
		
		public void setParent(Node parent) {
			this.parent = parent;
		}
		
		public Node getParent() {
			return parent;
		}

		public void setNr(int nr) {
			this.nr = nr;
		}

		public int getNr() {
			return nr;
		}

		private Dendrogram getOuterType() {
			return Dendrogram.this;
		}
		
		/**
		 * Zwraca nazwe dokumentu, jezeli w klastrze znajduje sie tylko jeden dokument
		 * @return
		 */
		public String getFirstDocumentTitle() {
			Set<DocumentForClustering> docs = cluster.getDocuments();
			if(docs != null && docs.size() == 1) {
				DocumentForClustering d = (DocumentForClustering) docs.toArray()[0];
				return d.getTitle();
			}
			return null;
		}

		public Double getSimilarity() {
			return similarity;
		}

		public void setSimilarity(Double similarity) {
			this.similarity = similarity;
		}

		@Override
		public int compareTo(Node o) {
			return nr - o.getNr();
		}
	}
}
